#include "TransformTool.h"

#define IS_SUCCESSFUL true
#define MSG_SUCCESS "Selection has been transformed"
#define MSG_FAIL_NOSELECTION "There is no selection"
#define MSG_FAIL "Selection has not been transformed"

/*constructor*/
TransformTool::TransformTool()
{
    rend = RenderManager::getInstance();
    global = GlobalStates::getInstance();
    mouseDown = false;
    mode = SCALE;
    axisConstraint = vector3<int>(1,1,1);
    scaleConstant = 0.2;
}

/*public methods*/
TransformTool *TransformTool::getInstance()
{
    static TransformTool* inst = 0;
    if(!inst)inst = new TransformTool();
    return inst;
}

string TransformTool::getType()
{
    return "transform";
}

void TransformTool::display()
{
    if(mouseDown && !initialSelection.empty())
    {
        set< vector3<int> >::iterator itr;
        switch(mode) {
        case TRANSLATE:
        {
            for (itr=initialSelection.begin(); itr != initialSelection.end(); itr++ ) {
                drawCoords = *itr;
                drawCoords += difference;
                Drawing::drawWireVoxel(drawCoords.x, drawCoords.y, drawCoords.z);
            }
            break;
        }
        case SCALE:
        {

            int scaleFactor = (int)difference.mag()*scaleConstant; //TODO: take into account direction
            vector3<int> selectionCentre = findSelectionCentre();
            for (itr=initialSelection.begin(); itr != initialSelection.end(); itr++ ) {
                vector3<int> v = *itr;
                vector3<int> w = v + vector3<int>(1,1,1);

                vector3<int> distv = (v - selectionCentre)*scaleFactor;
                vector3<int> distw = (w - selectionCentre)*scaleFactor;
                v = (selectionCentre + distv);
                w = (selectionCentre + distw);
                Drawing::drawWireCuboid(v,w,200,0,0,2);
            }
            break;
        }
        case ROTATE:
        {
            //stuff
            break;
        }
        }//end switch
    }
}

Result TransformTool::mousePressEvent(QMouseEvent *event)
{
    if (event->button()==Qt::LeftButton) {
        mouseDown = true;
        initialSelection = global->getSelection();
        startCoords = rend->mouseUtils.getMouseOverVoxelPt(event->x(), event->y());
    }
    return Result(IS_SUCCESSFUL);
}

Result TransformTool::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button()==Qt::LeftButton) {
        mouseDown = false;
        if(!initialSelection.empty())
        {
            set< vector3<int> >::iterator itr;
            for (itr=initialSelection.begin(); itr != initialSelection.end(); itr++ ) {
                vector3<int> voxelCoords = *itr;
                rend->removeVoxel(voxelCoords.x, voxelCoords.y, voxelCoords.z);
            }

            switch(mode) {
            case TRANSLATE:
                confirmTranslate();
                break;
            case SCALE:
                confirmScale();
                break;
            case ROTATE:
                //stuff
                break;
            }//end switch

            global->clearSelection();
            for (itr=newSelection.begin(); itr != newSelection.end(); itr++ ) {
                vector3<int> voxelCoords = *itr;
                global->addVoxelToSelection(voxelCoords.x, voxelCoords.y, voxelCoords.z);
            }
            newSelection.clear();
        }
    }
    return Result(IS_SUCCESSFUL);
}

Result TransformTool::mouseMoveEvent(QMouseEvent *event)
{
    if(mouseDown)
    {
        currentCoords = rend->mouseUtils.getMouseOverVoxelPt(event->x(), event->y());
        difference = ((currentCoords - startCoords));
        if(mode==TRANSLATE) {
            difference.x *= axisConstraint.x;
            difference.y *= axisConstraint.y;
            difference.z *= axisConstraint.z;
        }
    }
    return Result(IS_SUCCESSFUL);
}

Result TransformTool::keyPressEvent(QKeyEvent *event)
{
    int key = event->key();
    if(key == Qt::Key_X)
    {
        axisConstraint.x = 0;
        return Result(IS_SUCCESSFUL);
    } else if(key == Qt::Key_C)
    {
        axisConstraint.y = 0;
        return Result(IS_SUCCESSFUL);
    } else if(key == Qt::Key_Z)
    {
        axisConstraint.z = 0;
        return Result(IS_SUCCESSFUL);
    } else if(key == Qt::Key_Control)
    {
        toggleTranslateMode();
        return Result(IS_SUCCESSFUL);
    } else
    {
        //do nothing
        return Result(IS_SUCCESSFUL);
    }
}

Result TransformTool::keyReleaseEvent(QKeyEvent *event)
{
    int key = event->key();
    if(key == Qt::Key_X)
    {
        axisConstraint.x = 1;
        return Result(IS_SUCCESSFUL);
    } else if(key == Qt::Key_C)
    {
        axisConstraint.y = 1;
        return Result(IS_SUCCESSFUL);
    } else if(key == Qt::Key_Z)
    {
        axisConstraint.z = 1;
        return Result(IS_SUCCESSFUL);
    } else if(key == Qt::Key_Control)
    {
        toggleScaleMode();
        return Result(IS_SUCCESSFUL);
    } else
    {
        //do nothing
        return Result(IS_SUCCESSFUL);
    }
}

/*private methods*/
vector3<int> TransformTool::findSelectionCentre() {
    //using weighted average method
    vector3<int> total = vector3<int>(0,0,0);
    int count = 0;

    initialSelection = global->getSelection();
    set< vector3<int> >::iterator itr;
    for (itr=initialSelection.begin(); itr != initialSelection.end(); itr++ ) {
        vector3<int> voxelCoords = *itr;
        count++;
        total += voxelCoords;
    }
    return vector3<int>((total.x)/count,(total.y)/count,(total.z)/count);
}

void TransformTool::drawScaledVoxel(vector3<int> v, int k, vector3<int> centre)
{
    colour col = rend->getVoxelColor(v.x, v.y, v.z);
    vector3<int> w = v + vector3<int>(1,1,1);

    vector3<int> distv = (v - centre)*k;
    vector3<int> distw = (w - centre)*k;
    v = (centre + distv);
    w = (centre + distw);

    for(int i=v.x; i<=w.x; i++)
    {
        for(int j=v.y; j<=w.y; j++)
        {
            for(int k=v.z; k<=w.z; k++)
            {
                voxel m;
                m.setColor(col);
                rend->addVoxel(i, j, k, m);
                newSelection.insert(vector3<int>(i,j,k));
            }
        }
    }
    return;
}

void TransformTool::toggleTranslateMode()
{
    mode = TRANSLATE;
    return;
}

void TransformTool::toggleScaleMode()
{
    mode = SCALE;
    return;
}

void TransformTool::confirmTranslate()
{
    set< vector3<int> >::iterator itr;
    for (itr=initialSelection.begin(); itr != initialSelection.end(); itr++ ) {
        vector3<int> voxelCoords = *itr;
        colour col = rend->getVoxelColor(voxelCoords.x, voxelCoords.y, voxelCoords.z);
        voxelCoords += difference;
        voxel v;
        v.setColor(col);
        rend->addVoxel(voxelCoords.x, voxelCoords.y, voxelCoords.z, v);
        newSelection.insert(voxelCoords);
    }
}

void TransformTool::confirmScale()
{
    int scaleFactor = (int)difference.mag()*scaleConstant; //TODO: take into account direction
    vector3<int> selectionCentre = findSelectionCentre();
    set< vector3<int> >::iterator itr;
    for (itr=initialSelection.begin(); itr != initialSelection.end(); itr++ ) {
        vector3<int> tempCoords = *itr;
        drawScaledVoxel(tempCoords, scaleFactor, selectionCentre);
    }
}
